package com.pctx.bibawxminiprogram.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pctx.bibacommon.result.Result;
import com.pctx.bibacommon.util.DateTimeUtil;
import com.pctx.bibacommon.util.UuidUtil;
import com.pctx.bibawxminiprogram.dao.UserMapper;
import com.pctx.bibawxminiprogram.pojo.User;
import com.pctx.bibawxminiprogram.service.RedisService;
import com.pctx.bibawxminiprogram.service.UserService;
import com.pctx.bibawxminiprogram.service.WxApiService;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.codehaus.xfire.util.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.client.RestTemplate;
import com.pctx.bibawxminiprogram.util.RestClientUtil;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidParameterSpecException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 小程序的登录
 * Created by wnf on 2018/3/9.
 */
@Service("wxApplicationService")
@PropertySource("classpath:ev.properties")
public class WxApiServiceImpl implements WxApiService{

   private static Logger log = Logger.getLogger(WxApiService.class);

    @Autowired
    private RedisService redisService;
    @Autowired
    private UserMapper userMapper;

    // 微信小程序的id
    @Value("${appid}")
    private String appid;

    // 小程序的密钥
    @Value("${secret}")
    private String secret;

    // 用于获取openId的接口地址
    @Value("${jscode2SessionURL}")
    private String jscode2SessionURL;

    @Autowired
    private UserService userService;


    /**
     * 小程序获取openId
     *
     * @return
     * @throws Exception
     */
    public Map<String, Object> getOpenId(String js_code) throws Exception {
        String url = String.format(jscode2SessionURL, appid, secret, js_code);
        String data = RestClientUtil.getData(url, null);
        ObjectMapper om = new ObjectMapper();
        Map<String, Object> map = om.readValue(data, Map.class);
        return map;
    }

    /**
     * 通过code来获取openId和sessionKey并并返回生成的localSessionKey和用户的UUID
     * @param code 小程序前台传递过来的值
     * @return
     * @throws Exception
     */
    @Override
    public Result jscode2session(String code) throws Exception {
        if(StringUtils.isEmpty(code)){
            return Result.error("没有获取到前台传递过来的code值");
        }
        // 获取到用户的实际登录地址
        String accessToken = String.format(jscode2SessionURL, appid, secret, code);
        HttpGet httpGet = new HttpGet(accessToken);
        CloseableHttpResponse response = HttpClients.createDefault().execute(httpGet);
        HttpEntity entity = response.getEntity();
        String responseData = EntityUtils.toString(entity);

        // 解析返回的结果
        JSONObject jsonObject = JSONObject.parseObject(responseData);
        System.out.println(jsonObject);

        String sessionKey = null;
        String openid = null;
        if(jsonObject != null && jsonObject.getString("openid") != null){
            openid = jsonObject.getString("openid");
            if(jsonObject.containsKey("session_key")){
                sessionKey =  jsonObject.getString("session_key");
            }
            // 随机生成一个3rdSessionKey，然后返回给小程序前台使用
            String localSessionKey = RandomStringUtils.randomAlphanumeric(128);

            // 保存openID到数据库中
            Map<String,Object> map = new HashMap<>();
            // 根据openId去数据库中查找有没有此人的数据
            User loginUser = userService.getUserByOpenId(openid);
            if(loginUser != null){
                 String userUuid = loginUser.getUuid();
                 // 判断有没有保存到Redis中
                 if(redisService.get(localSessionKey) != null){
                     map.put("localSessionKey",localSessionKey);
                     map.put("userUuid",userUuid);
                     return Result.success(map);
                 }
                // 保存到Redis数据库中，key为localSessionKey，值为openid+sessionKey
                redisService.set(localSessionKey,openid+":"+sessionKey,108000L);
                map.put("localSessionKey",localSessionKey);
                 map.put("userUuid",userUuid);
                return Result.success(map);
            }else{
                User user = new User();
                user.setOpenid(openid);
                String userUuid = UuidUtil.randomUUID();
                user.setUuid(userUuid);
                user.setCreateTime(DateTimeUtil.nowTimestamp());
                user.setUpdateTime(DateTimeUtil.nowTimestamp());
                // 插入一条用户数据
                userMapper.insertSelective(user);
                // 保存到Redis数据库中，key为localSessionKey，值为openid+sessionKey
                redisService.set(localSessionKey,openid+":"+sessionKey,108000L);
                map.put("localSessionKey",localSessionKey);
                 map.put("userUuid",userUuid);
                // 返回localSession给小程序，然后跟服务器交互
                return Result.success(map);
            }
        }
        return Result.error("没有获取到前台传递过来的code值");
    }

    /**
     * 通过保存在本地的localSessionKey来获取openid
     * @param localSessionKey
     * @return
     * @throws Exception
     */
    @Override
    public String getOpenidByLocalSessionKey(String localSessionKey) throws Exception {
        if(StringUtils.isEmpty(localSessionKey)){
            return null;
        }
        String value = (String) redisService.get(localSessionKey);
        String[] arr = value.split(":");
        String openid = null;
        openid = arr[0];
        if(StringUtils.isEmpty(openid)){
            return null;
        }
        return openid;
    }

    /**
     * 通过保存在本地的localSessionKey来获取sessionKey
     * @param localSessionKey
     * @return
     * @throws Exception
     */
    public String getSessionKeyByLocalSessionKey(String localSessionKey) throws Exception{
        if(StringUtils.isEmpty(localSessionKey)){
            return null;
        }
        String value = (String) redisService.get(localSessionKey);
        String[] arr = value.split(":");

        String sessionKey = null;
        sessionKey = arr[1];
        if(StringUtils.isEmpty(sessionKey)){
            return null;
        }
        return sessionKey;
    }

    /**
     * 登录状态校验
     * @param localSessionKey
     * @return
     * @throws Exception
     */
    @Override
    public Result checkSession(String localSessionKey) throws Exception {

        if(StringUtils.isEmpty(localSessionKey)){
            return Result.error("还没有登录或者登录已失效");
        }

        String openid = getOpenidByLocalSessionKey(localSessionKey);

        if(StringUtils.isEmpty(openid)){
            return Result.success("还没有登录或者登录已失效");
        }
        return Result.success("已登录");
    }

    /**
     * 更新用户信息
     * @param userUuid
     * @param localSessionKey
     * @param nickName
     * @param avatarUrl
     * @param gender
     * @param province
     * @param city
     * @param country
     * @param rawData
     * @param signature
     * @return
     * @throws Exception
     */
    @Override
    public Result updateUserInfo(String userUuid, String localSessionKey, String nickName, String avatarUrl, String gender, String province, String city, String country, String rawData, String signature) throws Exception {
        if (StringUtils.isEmpty(localSessionKey)|| StringUtils.isEmpty(rawData) || StringUtils.isEmpty(signature)){
            return Result.error("数据缺失");
        }
        // 验证用户
        String openid = getOpenidByLocalSessionKey(localSessionKey);
        if (StringUtils.isEmpty(openid)){
            return Result.error("没有登录或者登录失效");
        }
        // 数据签名校验，https://mp.weixin.qq.com/debug/wxadoc/dev/api/signature.html
        // 其中 signature = sha1( rawData + session_key )
        String sessionKey = getSessionKeyByLocalSessionKey(localSessionKey);
        if (StringUtils.isEmpty(sessionKey))
            return Result.error("没有登录或者登录失效");

        if (StringUtils.isNotEmpty(rawData)) {
            try {
                rawData = URLDecoder.decode(rawData, StandardCharsets.UTF_8.name());
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        // 如果签名不一样，表示数据非法
        String localSignature = DigestUtils.sha1Hex(rawData + sessionKey);
        if (!signature.equals(localSignature))
            return Result.error("数据非法");

        if (StringUtils.isNotEmpty(nickName)) {
            try {
                nickName = URLDecoder.decode(nickName, StandardCharsets.UTF_8.name());
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        // 如果签名一样，表示数据合法，保存数据到数据库
        JSONObject userInfo = new JSONObject();
        userInfo.put("openid", openid);
        userInfo.put("nickname", nickName);
        userInfo.put("headimgurl", avatarUrl);
        userInfo.put("city", city);
        userInfo.put("country", country);
        userInfo.put("province", province);
        if (StringUtils.isNotEmpty(gender))
            userInfo.put("sex", Integer.parseInt(gender));
        // 通过用户的UUID来更新保存用户信息
        userService.updateUserInfo(userUuid, userInfo);
        return Result.success("更新用户信息成功");
    }

    /**
     * 绑定手机号码到指定的用户
     * @param userUuid
     * @param Mobile
     * @return
     * @throws Exception
     */
    @Override
    public Result bindMobileToUser(String userUuid, String Mobile) throws Exception {
        int row = userService.bindMobileToUser(userUuid, Mobile);
        if(row > 0){
            return Result.success("绑定手机号成功");
        }
        return Result.error("绑定手机号码失败");
    }

    /**
     * 解密用户敏感数据获取用户信息
     * @param encryptedData
     * @param localSessionKey
     * @param iv
     * @return
     */
    @Override
    public Result getUserInfo(String encryptedData, String localSessionKey, String iv) throws Exception {
        // 被加密的数据
        byte[] dataByte = Base64.decode(encryptedData);
        String sessionKey = getSessionKeyByLocalSessionKey(localSessionKey);
        byte[] keyByte = Base64.decode(sessionKey);
        byte[] ivByte = Base64.decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                JSONObject jsonObject = JSON.parseObject(result);
                // 返回解密后的用户信息
                return Result.success(jsonObject);
            }
        } catch (NoSuchAlgorithmException e) {
            log.error(e.getMessage(), e);
        } catch (NoSuchPaddingException e) {
            log.error(e.getMessage(), e);
        } catch (InvalidParameterSpecException e) {
            log.error(e.getMessage(), e);
        } catch (IllegalBlockSizeException e) {
            log.error(e.getMessage(), e);
        } catch (BadPaddingException e) {
            log.error(e.getMessage(), e);
        } catch (UnsupportedEncodingException e) {
            log.error(e.getMessage(), e);
        } catch (InvalidKeyException e) {
            log.error(e.getMessage(), e);
        } catch (InvalidAlgorithmParameterException e) {
            log.error(e.getMessage(), e);
        } catch (NoSuchProviderException e) {
            log.error(e.getMessage(), e);
        }
        return null;
    }


   /* @Override
    public Result getCaptchaByMobile(String Mobile) throws Exception {
        // 根据手机号码去调用短信验证的接口来获取验证码
        String res = restTemplate.getForObject("http://biba-sms/sms/ronglian?mobile=" + Mobile, String.class);

        if(StringUtils.isNotEmpty(res)){
            return Result.success(res);
        }
        return Result.error("请重新发送一次验证码");
    }*/

    /**
     * 绑定手机号
     * @param openid
     * @param mobile
     * @return
     * @throws Exception
     */
    @Override
    public Result bindMobile(String openid, String mobile) throws Exception {
        // 先根据openId取数据库找看看有没有此用户的注册
        User user = userService.getUserByOpenId(openid);
        if(user == null){
            User newUser = new User();
            newUser.setOpenid(openid);
            newUser.setMobile(mobile);
            String userUuid = UuidUtil.randomUUID();
            newUser.setUuid(userUuid);
            newUser.setCreateTime(DateTimeUtil.nowTimestamp());
            newUser.setUpdateTime(DateTimeUtil.nowTimestamp());
            userMapper.insertSelective(newUser);
            Map<String,Object> map = new HashMap<>();
            map.put("userUuid",userUuid);
            map.put("success",0);
            return Result.success(map);
        } else {
            // 如果该微信用户已经注册，直接返回已经注册的微信用户的手机号码
            Map<String,Object> map = new HashMap<>();
            map.put("userUuid",user.getUuid());
            map.put("success",0);
            return Result.success(map);
        }
    }

    /**
     * 根据openId去取用户信息
     * @param openid
     * @return
     * @throws Exception
     */
    @Override
    public Result getUserInfoByOpenid(String openid) throws Exception {
        // 根据openId去数据库查询是否有该openId的用户
        User user = userService.getUserByOpenId(openid);
        Map<String,Object> map = new HashMap<>();
        if(user != null){
            String userUuid = user.getUuid();
            String mobile = user.getMobile();
            map.put("userUuid",userUuid);
            map.put("mobile",mobile);
            map.put("success",0);
            return Result.success(map);
        }
        return Result.error("用户没有注册");
    }

}